package team

import (
	"fmt"
	"sync"
	"time"

	"xagent/internal/actors"
	"xagent/internal/knowledge"
	proto "xagent/proto/v1"

	"github.com/asynkron/protoactor-go/actor"
	"github.com/sirupsen/logrus"
)

// Team represents a team of AI agents
type Team struct {
	name       string
	agents     map[string]*actors.AIAgent
	agentPIDs  map[string]*actor.PID
	rag        *knowledge.RAGEngine
	mutex      sync.RWMutex
	system     *actor.ActorSystem
	supervisor *actor.PID
}

// NewTeam creates a new team
func NewTeam(name string, system *actor.ActorSystem, rag *knowledge.RAGEngine) *Team {
	return &Team{
		name:      name,
		agents:    make(map[string]*actors.AIAgent),
		agentPIDs: make(map[string]*actor.PID),
		rag:       rag,
		system:    system,
	}
}

// Hire adds an agent to the team
func (t *Team) Hire(agent *actors.AIAgent) error {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	if _, exists := t.agents[agent.GetRole()]; exists {
		return fmt.Errorf("agent with role %s already exists", agent.GetRole())
	}

	logrus.WithFields(logrus.Fields{
		"agent_role": agent.GetRole(),
		"team":       t.name,
	}).Info("Hiring new agent")

	// Spawn the agent actor
	props := actor.PropsFromProducer(func() actor.Actor {
		return agent
	})
	pid := t.system.Root.Spawn(props)

	logrus.WithFields(logrus.Fields{
		"agent_role": agent.GetRole(),
		"pid":        pid.String(),
	}).Debug("Agent actor spawned")

	// Store both the agent and its PID
	t.agents[agent.GetRole()] = agent
	t.agentPIDs[agent.GetRole()] = pid
	return nil
}

// AssignTask assigns a task to an agent
func (t *Team) AssignTask(task *proto.Task) error {
	agent, err := t.findBestAgent(task)
	if err != nil {
		logrus.WithError(err).Error("Failed to find best agent for task")
		return err
	}

	logrus.WithFields(logrus.Fields{
		"task_id":     task.Id,
		"task_name":   task.Name,
		"assigned_to": agent.GetRole(),
		"description": task.Description,
	}).Info("Assigning task to agent")

	// Create task message
	msg := &proto.AgentMessage{
		Id:   task.Id,
		Type: proto.MessageType_MESSAGE_TYPE_REQUEST,
		Content: &proto.AgentMessage_Message{
			Message: task.Description,
		},
		Metadata: map[string]string{
			"type": "task_assignment",
			"team": t.name,
		},
	}

	// Get agent's PID and send message
	if pid, ok := t.agentPIDs[agent.GetRole()]; ok {
		logrus.WithFields(logrus.Fields{
			"task_id": task.Id,
			"agent":   agent.GetRole(),
			"pid":     pid.String(),
		}).Debug("Sending task to agent")
		t.system.Root.Send(pid, msg)
		return nil
	}
	return fmt.Errorf("agent PID not found for role %s", agent.GetRole())
}

// ProcessPendingTasks processes all pending tasks
func (t *Team) ProcessPendingTasks() error {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	for _, agent := range t.agents {
		if err := t.processPendingTasksForAgent(agent); err != nil {
			return err
		}
	}

	return nil
}

// processPendingTasksForAgent processes pending tasks for a specific agent
func (t *Team) processPendingTasksForAgent(agent *actors.AIAgent) error {
	// Get tasks from agent's mailbox
	if parent := agent.GetContext().Parent(); parent != nil {
		// TODO: Implement task retrieval from agent's mailbox
		// For now, we'll just return nil as this needs to be implemented
		// based on the actor system's message handling
		return nil
	}
	return nil
}

// HireStandardTeam hires a standard team of agents
func (t *Team) HireStandardTeam() error {
	// Create tester agent
	tester := actors.NewAIAgent("tester-1", "Software testing specialist")
	tester.SetKnowledgeBase(t.rag)
	if err := t.Hire(tester); err != nil {
		return err
	}

	// Create QA engineer agent
	qa := actors.NewAIAgent("qa-1", "Quality assurance specialist")
	qa.SetKnowledgeBase(t.rag)
	if err := t.Hire(qa); err != nil {
		return err
	}

	// Create DevOps engineer agent
	devops := actors.NewAIAgent("devops-1", "Infrastructure and deployment specialist")
	devops.SetKnowledgeBase(t.rag)
	if err := t.Hire(devops); err != nil {
		return err
	}

	return nil
}

// findBestAgent finds the most suitable agent for a task
func (t *Team) findBestAgent(task *proto.Task) (*actors.AIAgent, error) {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	var bestAgent *actors.AIAgent
	var bestScore float64

	logrus.WithFields(logrus.Fields{
		"task_id":   task.Id,
		"task_name": task.Name,
	}).Debug("Finding best agent for task")

	for _, agent := range t.agents {
		score := t.calculateAgentScore(agent, task)
		logrus.WithFields(logrus.Fields{
			"agent_role": agent.GetRole(),
			"score":      score,
		}).Trace("Agent score calculated")

		if score > bestScore {
			bestScore = score
			bestAgent = agent
		}
	}

	if bestAgent == nil {
		return nil, fmt.Errorf("no suitable agent found for task")
	}

	logrus.WithFields(logrus.Fields{
		"selected_agent": bestAgent.GetRole(),
		"score":          bestScore,
	}).Debug("Best agent selected for task")

	return bestAgent, nil
}

// calculateAgentScore calculates how suitable an agent is for a task
func (t *Team) calculateAgentScore(agent *actors.AIAgent, task *proto.Task) float64 {
	// Base score starts at 1.0
	score := 1.0

	// Check agent capabilities
	for _, cap := range agent.GetCapabilities() {
		if task.Metadata != nil {
			if _, ok := task.Metadata[cap]; ok {
				score += 0.5
			}
		}
	}

	return score
}

// GetAgentByRole returns an agent by role
func (t *Team) GetAgentByRole(role string) (*actors.AIAgent, error) {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	if agent, ok := t.agents[role]; ok {
		return agent, nil
	}
	return nil, fmt.Errorf("agent with role %s not found", role)
}

// GetAllAgents returns all agents in the team
func (t *Team) GetAllAgents() []*actors.AIAgent {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	agents := make([]*actors.AIAgent, 0, len(t.agents))
	for _, agent := range t.agents {
		agents = append(agents, agent)
	}
	return agents
}

// GetTeamMetrics returns team performance metrics
func (t *Team) GetTeamMetrics() map[string]interface{} {
	metrics := make(map[string]interface{})
	metrics["agent_count"] = len(t.agents)
	metrics["last_update"] = time.Now()
	return metrics
}
